package com.unknow.first.dapp.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lianziyou.chain.common.utils.ConvertContractValue;
import com.unknow.first.dapp.admin.config.AddressInfoProperties;
import com.unknow.first.dapp.admin.mapper.TAddressInfo;
import com.unknow.first.dapp.admin.mapper.TAddressTree;
import com.unknownman.block.entity.TContractLogs;
import com.unknownman.block.service.TContractLogsService;
import lombok.extern.slf4j.Slf4j;
import org.cloud.core.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StringUtils;

import java.util.List;

@Component
@Slf4j
public class AddressContractLogsService {

    @Autowired
    TAddressInfoService addressInfoService;

    @Autowired
    TAddressTreeService addressTreeService;

    @Autowired
    AddressInfoProperties addressInfoProperties;

    @Autowired
    private DataSourceTransactionManager transactionManager;


    @Autowired
    RedisUtil redisUtil;

    @Autowired
    private TContractLogsService contractLogsService;

    final String startIdKey = "system:land-admin:job:TUserAddressJob.startId";

    public void scanLogsToAddressTable() {
        Long startId = redisUtil.get(startIdKey);

        if (startId == null) {
            startId = 0L;
        }
        QueryWrapper<TContractLogs> where = new QueryWrapper<>();
        where.eq(!StringUtils.isEmpty(addressInfoProperties.getAddress()), "contract_address", addressInfoProperties.getAddress());
        where.eq(!StringUtils.isEmpty(addressInfoProperties.getFirstTopic()), "first_topic", addressInfoProperties.getFirstTopic());
        where.gt(!StringUtils.isEmpty(startId), "contract_log_id", startId);
        where.orderByAsc("block_timestamp", "index_for_block", "contract_log_id");
        List<TContractLogs> contractLogs = contractLogsService.list(where);

        try {
            for (TContractLogs contractLog : contractLogs) {
                startId = insertAddressInfo(startId, contractLog);
            }
        } finally {
            redisUtil.set(startIdKey, startId);
        }
    }

    Long insertAddressInfo(Long startId, TContractLogs contractLog) {
        TAddressInfo addressInfo = new TAddressInfo();

        final String childAddress = ConvertContractValue.convertToAddress(contractLog.getSecondTopic());
        final String parentAddress = ConvertContractValue.convertToAddress(contractLog.getThirdTopic());

        addressInfo.setAddress(childAddress);
        addressInfo.setParentAddress(parentAddress);
        addressInfo.setBlockTimestamp(contractLog.getBlockTimestamp());
        addressInfo.setRpcAddress(contractLog.getRpcAddress());

        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            addressInfoService.getBaseMapper().insert(addressInfo);

            TAddressTree addressTree = new TAddressTree();
            addressTree.setChildAddress(childAddress);
            addressTree.setParentAddress(childAddress);
            addressTree.setDistance(0);
            addressTreeService.getBaseMapper().insert(addressTree);

            addressTree = new TAddressTree();
            addressTree.setChildAddress(childAddress);
            addressTree.setParentAddress(parentAddress);
            addressTree.setDistance(1);
            addressTreeService.getBaseMapper().insert(addressTree);

            QueryWrapper<TAddressTree> treeWhere = new QueryWrapper<>();
            treeWhere.eq("child_address", parentAddress);
            treeWhere.gt("distance", 0);

            List<TAddressTree> childTreeResult = addressTreeService.list(treeWhere);

            for (TAddressTree addressTreeByParent : childTreeResult) {
                addressTree.setId(null);
                addressTree.setChildAddress(childAddress);
                addressTree.setParentAddress(addressTreeByParent.getParentAddress());
                addressTree.setDistance(addressTreeByParent.getDistance() + 1);
                addressTreeService.getBaseMapper().insert(addressTree);
            }
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            e.printStackTrace();
        }

        if (contractLog.getContractLogId() > startId) {
            startId = contractLog.getContractLogId();
        }
        return startId;
    }
}
